package recipe;

/**
 * Class - Ingredient
 * Description - Class to represent an ingredient
 * @author Ryne Okimoto
 */

public class Ingredient {
	/*
	 * 1 cup = 8 fl oz
	 * 1 pint = 2 cups
	 * 1 quart = 2 pints
	 * 4 cups = 1 quart
	 * 1 gallon = 4 quarts
	 * 
	 * 16 oz = 1 lb
	 * ***********************
	 * 
	 * standard = metric
	 * 
	 * fluid oz = mL, multiply oz by 30 to get mL
	 * gal = L, multiply gal by 3.8 to get L
	 * qt = L, multiply qt by 0.9 to get L
	 * 
	 * oz = g, multiply oz by 28 to get g
	 * lb = kg, multiply by 0.45 to get kg
	 * 
	 */
	
	public static final String UNIT_QUANTITY = "";
	
	public static final String UNIT_METRIC_ML = "mL";
	public static final String UNIT_METRIC_L = "L";
	public static final String UNIT_METRIC_GRAM = "g";
	public static final String UNIT_METRIC_KILOGRAM = "kg";
	
	public static final String UNIT_STANDARD_FL_OZ = "fl oz";
	public static final String UNIT_STANDARD_CUP = "c";
	public static final String UNIT_STANDARD_PINT = "pt";
	public static final String UNIT_STANDARD_QUART = "qt";
	public static final String UNIT_STANDARD_GALLON = "gal";
	public static final String UNIT_STANDARD_TEASPOON = "tsp";
	public static final String UNIT_STANDARD_TABLESPOON = "Tbsp";
	public static final String UNIT_STANDARD_OZ = "oz";
	public static final String UNIT_STANDARD_POUND = "lb";
	
	// Ingredient attributes
	private String ingredientName;
	private String ingredientAmount;
	private String ingredientUnit;
	private boolean isMetric;
	// TODO implement to help with scaling
	// private boolean amountIsFraction;
	// private boolean amountIsDecimal;
	// private boolean amountIsInteger;
	
	/**
	 * Default constructor for Ingredient class
	 */
	public Ingredient(){
		ingredientName = null;
		ingredientAmount = null;
		ingredientUnit = null;
		isMetric = false;
	} // constructor
	
	/**
	 * Overloaded constructor for Ingredient class
	 * 
	 * Description: Initializes data fields for Ingredient
	 *     Assumes all fields are correct
	 * @param name
	 * @param amount
	 * @param unit
	 */
	public Ingredient(String name, String amount, String unit){
		ingredientName = name;
		ingredientAmount = amount;
		ingredientUnit = unit;
		isMetric = isUnitMetric(unit);
	} // constructor
	
	/**
	 * Method - toString
	 * Description - Generates a string representation of the ingredient
	 * @override
	 */
	public String toString(){
		return 
			ingredientAmount + " " + 
			ingredientUnit + " " +
			ingredientName;
	}
	
	/**
	 * Method - validateUnit
	 * Description - Checks if a unit is valid
	 * @param unit
	 * @return True - Returns if the unit is valid
	 * @return False - Returns if the unit is not valid
	 */
	public static boolean validateUnit(String unit){
		return 
			unit.equals(UNIT_METRIC_ML) ||
			unit.equals(UNIT_METRIC_L) ||
			unit.equals(UNIT_METRIC_GRAM) ||
			unit.equals(UNIT_METRIC_KILOGRAM) ||
			unit.equals(UNIT_STANDARD_CUP) ||
			unit.equals(UNIT_STANDARD_FL_OZ) ||
			unit.equals(UNIT_STANDARD_GALLON) ||
			unit.equals(UNIT_STANDARD_OZ) ||
			unit.equals(UNIT_STANDARD_PINT) ||
			unit.equals(UNIT_STANDARD_POUND) ||
			unit.equals(UNIT_STANDARD_QUART) ||
			unit.equals(UNIT_STANDARD_TABLESPOON) ||
			unit.equals(UNIT_STANDARD_TEASPOON) ||
			unit.equals(UNIT_QUANTITY);
	}
	
	/**
	 * Method - parseAmount
	 * Description - Parses a fraction or decimal string into a double
	 * @param amount
	 * @return 0 - if the amount is not a valid fraction or decimal
	 * @return double - the amount as a decimal if the string is a valid fraction or decimal
	 */
	public static double parseAmount(String amount){
		double num = 0, den = 0;
		String[] fraction = null;
		double value = 0;
		boolean isMatch = false;
		boolean isFraction = false;
		boolean isDecimal = false;
		boolean isInteger = false;
		
		// Integer - one or more digits
		isInteger = amount.matches("\\d++");
		// Decimal number - one or more digits 
		// followed by a period followed by 
		// one or more digits
		isDecimal = amount.matches("\\d++\\.\\d++");
		// Fraction - one or more digits 
		// followed by a '/' followed by 
		// one or more digits
		isFraction = amount.matches("\\d++/\\d++");
		
		isMatch = isInteger || isDecimal || isFraction;	
		
		if(isMatch){
			if(isInteger){
				value = Integer.parseInt(amount);
			}
			else if(isDecimal){
				value = Double.parseDouble(amount);
			}
			else if(isFraction){
				fraction = amount.split("/");
				num = Double.parseDouble(fraction[0]);
				den = Double.parseDouble(fraction[1]);
				value = num / den;
			}
		}
		
		return value;
	}
	
	public static String formatDouble(double d){
		return String.format("%1$.2f", d);
	}
	
	/**
	 * Method - getUnits
	 * Description - Returns an array of the valid units
	 * @return String[] - The array of units
	 */
	public static String[] getUnits(){
		String[] units = {
			UNIT_QUANTITY,
			UNIT_METRIC_ML,
			UNIT_METRIC_L,
			UNIT_METRIC_GRAM,
			UNIT_METRIC_KILOGRAM,
			UNIT_STANDARD_FL_OZ,
			UNIT_STANDARD_CUP,
			UNIT_STANDARD_PINT,
			UNIT_STANDARD_QUART,
			UNIT_STANDARD_GALLON,
			UNIT_STANDARD_TEASPOON,
			UNIT_STANDARD_TABLESPOON,
			UNIT_STANDARD_OZ,
			UNIT_STANDARD_POUND
		};
		
		return units;
	}
	
	/**
	 * Method - isUnitMetric
	 * Description - Checks if a unit is metric
	 * @param unit
	 * @return True - Returns if the unit is metric
	 * @return False - Returns if the unit is standard
	 */
	private boolean isUnitMetric(String unit){
		return unit.equals(UNIT_METRIC_L) || unit.equals(UNIT_METRIC_ML) ||
			unit.equals(UNIT_METRIC_GRAM) || unit.equals(UNIT_METRIC_KILOGRAM);
	}
	
	/**
	 * Method - scaleAmount
	 * @param multiplier
	 * @return 0 - Returns if the amount cannot be scaled
	 * @return double - The scaled amount
	 */
	public double scaleAmount(int multiplier){
		double result = 0;
		
		result = parseAmount(this.ingredientAmount) * multiplier;
		
		if(result != 0){
			this.ingredientAmount = formatDouble(result);
		}
		
		return result;
	} //scaleAmount
	
	/**
	 * Method - convertUnit
	 * Description - Converts between standard and metric units
	 * @return double - the new value of the amount
	 * @return 0 - if there is an error during conversion
	 */
	public double convertUnit(){
		double result = 0;
		
		if(isMetric){
			result = convertMetric();
			isMetric = false;
		}
		else{
			result = convertStandard();
			isMetric = true;
		}
		
		if(result != 0){
			// Sets the new ingredient amount if conversion succeeds
			ingredientAmount = formatDouble(result);
		}
		
		return result;
	}
	
	/**
	 * Method - convertStandard
	 * Description - Converts a standard unit to metric
	 * @return double - the new value of the amount
	 * @return 0 - if there is a conversion error due to parsing the amount
	 */
	private double convertStandard(){
		double result = 0;
		if(ingredientUnit.equals(UNIT_STANDARD_FL_OZ)){
			// Convert mL to fl oz
			// 1 mL = 0.033814 fl oz, source Google
			result = parseAmount(ingredientAmount) / 0.033814;
			ingredientUnit = UNIT_METRIC_ML;
		}
		else if(ingredientUnit.equals(UNIT_STANDARD_GALLON)){
			// Convert L to gal
			// 1 L = 0.264172 gal, source Google
			result = parseAmount(ingredientAmount) / 0.264172;
			ingredientUnit = UNIT_METRIC_L;
		}
		else if(ingredientUnit.equals(UNIT_STANDARD_OZ)){
			// Convert g to oz
			// 1 g = 0.035274 oz, source Google
			result = parseAmount(ingredientAmount) / 0.035274;
			ingredientUnit = UNIT_METRIC_GRAM;
		}
		else if(ingredientUnit.equals(UNIT_STANDARD_POUND)){
			// Convert kg to lb
			// 1 kg = 2.20462 lb, source Google
			result = parseAmount(ingredientAmount) / 2.20462;
			ingredientUnit = UNIT_METRIC_KILOGRAM;
		}
		return result;
	}
	
	/**
	 * Method - convertMetric
	 * Description - Converts a metric unit into its standard counterpart
	 * @return double - the new value of the amount
	 */
	private double convertMetric(){
		double result = 0;
		if(ingredientUnit.equals(UNIT_METRIC_ML)){
			// Convert mL to fl oz
			// 1 mL = 0.033814 fl oz, source Google
			result = parseAmount(ingredientAmount) * 0.033814;
			ingredientUnit = UNIT_STANDARD_FL_OZ;
		}
		else if(ingredientUnit.equals(UNIT_METRIC_L)){
			// Convert L to gal
			// 1 L = 0.264172 gal, source Google
			result = parseAmount(ingredientAmount) * 0.264172;
			ingredientUnit = UNIT_STANDARD_GALLON;
		}
		else if(ingredientUnit.equals(UNIT_METRIC_GRAM)){
			// Convert g to oz
			// 1 g = 0.035274 oz, source Google
			result = parseAmount(ingredientAmount) * 0.035274;
			ingredientUnit = UNIT_STANDARD_OZ;
		}
		else if(ingredientUnit.equals(UNIT_METRIC_KILOGRAM)){
			// Convert kg to lb
			// 1 kg = 2.20462 lb, source Google
			result = parseAmount(ingredientAmount) * 2.20462;
			ingredientUnit = UNIT_STANDARD_POUND;
		}
		
		return result;
	}
	
	/**
	 * Method - setIngredientName
	 * Description - Setter for the ingredient name
	 * @param name
	 */
	public void setIngredientName(String name){
		ingredientName = name;
	}
	
	/**
	 * Method - getIngredientName
	 * Description - Getter for the ingredient name
	 * @return String - The ingredient name
	 */
	public String getIngredientName(){
		return ingredientName;
	}
	
	/**
	 * Method - setAmount
	 * Description - Setter for the ingredient amount
	 * @param amount
	 */
	public void setAmount(String amount){
		ingredientAmount = amount;
	}
	
	/**
	 * Method - getAmount
	 * Description - Getter for the ingredient amount
	 * @return String - The amount
	 */
	public String getAmount(){
		return ingredientAmount;
	}
	
	/**
	 * Method - setUnit
	 * Description - Setter for the unit
	 * @param unit
	 */
	public void setUnit(String unit){
		ingredientUnit = unit;
	}
	
	/**
	 * Method - getUnit
	 * Description - Getter for the unit
	 * @return String - The unit
	 */
	public String getUnit(){
		return ingredientUnit;
	}
}
